If you cast your mind back you might recall our golden style sheets rule, separate appearance from content. Suddenly we are talking about generated content. You might be thinking, doesn't this introduce content into our style sheets? What about the golden rule? Well, it's a good question, and this area is one of controversy among style sheets experts. I won't buy into that debate here, but I want to cover the issue of generated content in CSS2 in this section.
Firstly, note that some kinds of generated content already exist in CSS1. Think about bulleted and numbered lists. These add numbering or bullets, so they do generate content to an extent. CSS2 adds more powerful ways of generating content. Here we learn how.
There are two aspects to generating content with CSS2.
Step one uses the two new CSS2 pseudo classes we saw a little earlier. The before and after pseudo classes are appended to selectors, and allow the insertion before or after selected elements. For example, you might want to prepend the string "Chapter: " before all headings of level 1. The pseudo element selector to do this is H1:before
.
We then need to tell the browser what text to prepend. To do this we use a new property, content.
The content property allows us to define the content that is to be inserted before or after selected elements. Content can comprise strings of text, images, counters and quotation marks.
Quotation marks are defined by the new quotes property. This property allows different quotation marks to be created for different elements (for example when quoting different languages the correct quotation marks for that language will be inserted automatically.)
The content property specifies the content to be added before or after an element selected by the before and after pseudo classes. It can only be used with these selectors.
content can comprise a list of strings, URLs that resolve to images or other resources, counters or keyword defined elements. These are added as inline, block or marker elements before or after selected elements depending on the pseudo element selector used. However the statement that creates the content can set the display of the content to be a block, or other kind of element, rather than an inline element. For example
BODY:after {
content: "The End";
display: block;
margin-top: 2em;
text-align: center;
}
places the text string "The End" at the foot of the page as a block, rather than as an inline element.
Keyword values include open-quote, close-quote, no-open-quote, no-close-quote.
open-quote places the open quote string as defined by the quotes property for the elements (see below).
close-quote places the close quote string as defined by the quotes property for the elements (see below).
no-open-quote does not insert any content, but increments the level of nesting of the quotes.
no-close-quote does not insert any content but decrements the level of nesting of the quotes.
For more on quotes, see the quotes property below.
counter values are more than a little involved. Think of them as super-charged versions of numbered lists. Counters have a name, and a style. You create a counter using the counter function, then use them in conjunction with the content property to insert markers into a document.
There are two forms of the counter function, the counter form and the counters form.
The counter function has the form counter(identifier, list-style). The identifier is the name of the counter whose value you want to insert, while the (optional) list style specifies what style of counter you want (for instance lower case roman.) These styles are the same as those for elements of type list-style. When the browser encounters a counter function, it determines the value of that counter at this point of the document, then inserts it into the document in the nominated style. How does it determine this value? See the section on counters below for more detail.
The counters form is very similar, but allows for multiple counters to be inserted simultaneously. The form is counters(identifier, string, list-style). This returns all counters with the name specified by the identifier at this point of the document in the given list style, separated by the string. For more on this rather sophisticated aspect of CSS2 see the counters section below.
The last possible value for content, and unique to the content property is the attr() function. This returns the value of a named attribute of the selected element. The returned value is then inserted as a string. For example, with a link, the title of the link could be inserted after the link in the following way
A:after {
content: attr(title)
}
This might be used in conjunction with an @media rule to specify that only when printing a page, the URL should be inserted after the link.
@media all
{A:after{
content: attr(HREF)
}
}
If no content is specified, the default value is an empty string.
An element does not inherit the content property of the element which contains it.
The quotes property specifies the quote marks to be used for different levels of quotation. It is used in conjunction with the content property we saw above. When content takes a value of open-quote and close-quote, the open and close quotes inserted are those defined by the quotes property.
quotes are defined as a list of pairs of strings. Each pair defines respectively the opening and the closing quotation mark. The pairs are separated by spaces, as is each element of a pair. For example the following statements create different quotes for different languages, nested two levels deep.
/* Specify pairs of quotes for two levels in two languages */
Q:lang(en) { quotes: '"' '"' "'" "'" }
Q:lang(fr) { quotes: "«" "»" "<" ">" }
In English, according to this statement, first level quotes are double quoted, while second level quotes are single quoted. In French, first level quotes use "«" and "»" while second level quotes use "<" and ">".
If no quotes is specified, the default value is determined by the browser.
An element inherits the quotes property of the element which contains it.
Counters are sophisticated and powerful, and not surprisingly are a little complex. Here's how they work.
Counters are used exclusively by the content property to insert numbering (e.g. "1.5") before or after an element. You don't need to "create" a counter, just by referring to them they come into existence. For instance, let's suppose you created the simple rule below
h1:before {content: counter(chapter)}
This tells the browser to add the value of the counter called "chapter" before every heading of level one. But what value is that? Well, if we don't set the value using the properties we will discuss in a minute, the counter value will be 0. So every heading of level one will be preceded by a 0. We don't really want that. We want something along the lines of the first heading to be preceded by a 1, the second by a 2, and so on. To do this, we use the counter-increment property. This increments the value of a counter by a given value (you can jump up by 2, down by 1 (using the value of -1), as well as simply incrementing by one.)
To use counter-increment, name the counter you want to increment using the identifier for the counter, and then the value you want to increment by. (Again, simply using the property "creates" a counter with that identifier if it doesn't already exist). In the following example, we increment the value of the "chapter" counter for each h1 element.
h1 {counter-increment: chapter 1}
You can of course combine the two rules into this simple rule
h1:before {content: counter(chapter);
counter-increment: chapter 1
}
This increments the counter then adds it before the h1 element. Note the special case that if you increment and use a counter in the same rule, it is incremented before being inserted even if the content property comes before the increment property.
What does this let us achieve? Well, now, instead of marking up the headings of each chapter in our HTML based document like this
<h1>1. Abstract</h1>
...
<h1>2. Introduction</h2>
We leave out the numbering and it is done automatically. This aids the editing and writing processes, because moving sections, subsections and so on, or deleting them, or inserting new sections doesn't necessitate re-entering every number. It's particularly useful where a document might be served up in pieces based on the user's needs, or the medium they are using.
Let's look at a more complex example, using subsections. Here we will also take a look at the counter-reset property. Any non-trivial piece of documentation will almost certainly be divided into sections and subsections. We've seen how to number the top level sections (we called them chapters), but what about subsections? The particular problem is resetting the counter at the beginning of a new subsection.
Here is what we do
h1:before {content: counter(chapter);
counter-increment: chapter 1
/* this is the same as the example above */
counter-reset: subsection 0
}
In this example, we reset the counter called subsection to 0 each time we start a new section (assuming the h1 is the start of a new section). Now we need to increment the subsection counter for each subsection (heading of level 2), then insert the subsection content so that our subsections are correctly numbered. Let's take a look.
h2:before {content: counter(chapter) "." counter(subsection);
counter-increment: subsection 1
}
This increments the counter called subsection, then inserts numbering of the form "2.5" before the text of the heading.
The last thing we need to learn about counters is a little more involved again. Think about lists in HTML. These can contain other lists, which can contain others, and so on ad infinitum. It's not possible in advance to know how many levels there will be, so how do we take care of this situation? Fortunately those smart people at the W3C thought of this, and provide a solution. It's a little tricky, but not rocket science. Counters have a particular "scope". When a counter is created its scope is the level at which it was created, and all that level's children. However, where a counter of the same name is created inside the scope of the initial counter, then this is treated as a separate counter, which now takes over the counting. Confusing? Let's take a look at an example, taken from the CSS2 specification.
Here's the style sheet rule that creates the counter
OL { counter-reset: item } LI { display: block } LI:before { content: counter(item) ". "; counter-increment: item }
And here is the HTML
<OL> <!-- (set item[0] to 0 --> <LI>item <!-- increment item[0] (= 1) --> <LI>item <!-- increment item[0] (= 2) --> <OL> <!-- (set item[1] to 0 --> <LI>item <!-- increment item[1] (= 1) --> <LI>item <!-- increment item[1] (= 2) --> <LI>item <!-- increment item[1] (= 3) --> <OL> <!-- (set item[2] to 0 --> <LI>item <!-- increment item[2] (= 1) --> </OL> <!-- ) --> <OL> <!-- (set item[3] to 0 --> <LI> <!-- increment item[3] (= 1) --> </OL> <!-- ) --> <LI>item <!-- increment item[1] (= 4) --> </OL> <!-- ) --> <LI>item <!-- increment item[0] (= 3) --> <LI>item <!-- increment item[0] (= 4) --> </OL> <!-- ) --> <OL> <!-- (reset item[4] to 0 --> <LI>item <!-- increment item[4] (= 1) --> <LI>item <!-- increment item[4] (= 2) --> </OL> <!-- ) -->
Each OL "creates" a counter called "item". Every list item inside the OL increments the counter, then inserts the number before itself. However, when we get to an OL inside another, a new counter called "item" is created. The other ones don't go away, they simply remain unchanged until their scope is again relevant. Note that they all have the same name. You might think this is a recipe for disaster, but there is a good reason for this.
Here's one last string to the counters and content bow. Imagine you want to insert the "full" number for every list item (for example 2.5.1). With the counter function you can only insert the value for one named counter. In this case it would be the value for the counter named item at the level you are currently at. How do we get all of the counters with that name which are relevant at the level we are at? With the counters function. We saw this in the section on content above, but let's revisit it. It has the form counters(identifier, string, list-style), (where the list-style is optional) and it does exactly what we want. It returns every counter in the document relevant at the point it is called, separated by the string you give, in the style specified. What do I mean by "relevant at this point?" Remember that the scope of a counter is inside the element in which it is created, including all of its children. Basically, a counter is relevant inside any of the children of the element which created it.
To cut a long story short, its as simple as counters(item, ".")
The counter-reset property sets the value of a named counter.
counter-reset takes the name of the counter to reset. Counter names are identifiers, and comprise letters, numerals and hyphens. They cannot begin with numerals or hyphens. It also takes an optional integer, to which it sets the value of the identified counter.
counter-reset can also take a list of identifiers and optional integers, to set the value of several counters simultaneously. This is no mere luxury. Because of cascading, the following will set only the counter named counter2.
H1 { counter-reset: counter1 -1 }
H1 { counter-reset: counter2 99 }
In order to reset both, the following must be used.
H1 { counter-reset: counter1 -1 counter2 99 }
counter-reset can also be set to none.
If no counter-reset is specified, the default value is none.
An element does not inherit the counter-reset property of the element which contains it.
The counter-increment property increments the value of a named counter by a certain amount.
counter-increment takes the name of the counter to increment. Counter names are identifiers, and comprise letters, numerals and hyphens. They cannot begin with numerals or hyphens. It also takes an optional integer, by which it increments the value of the identified counter. If no integer is specified, the counter is incremented by one.
counter-increment can also take a list of identifiers and optional integers, to set the value of several counters simultaneously. This is no mere luxury. Because of cascading, the following will set only the counter named counter2.
H1 { counter-increment: counter1 -1 }
H1 { counter-increment: counter2 99 }
In order to reset both, the following must be used.
H1 { counter-increment: counter1 -1 counter2 99 }
counter-increment can also be set to none.
If no counter-increment is specified, the default value is none.
An element does not inherit the counter-increment property of the element which contains it.
Now that we've covered the latest in CSS2, we'll close by taking a look at some "advanced" aspects of style sheets. We'll cover some features of CSS that we haven't had the time to go into so far.
|